/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.builder;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import org.balisunrise.annotation.util.ColumnUtilities;
import org.balisunrise.application.Application;
import org.balisunrise.application.VisualEntityManager;
import org.balisunrise.common.*;
import org.balisunrise.common.defaults.*;
import org.balisunrise.common.events.*;
import org.balisunrise.common.events.VisualActionEvent;

//TODO DOCUMENTACAO da CommonFactory

/**
 *
 * @author Glauco
 */
public class CommonFactory {

    private static CommonFactory factory;

    private CommonFactory(){};

    /**
     *
     * @return
     */
    public static CommonFactory getFactory(){

        if(factory == null)
            factory = new CommonFactory();

        return factory;
    }

    /**
     * 
     * @return
     */
    public EntityList createEntityList(){

        DefaultEntityList entityList = new DefaultEntityList();
        return entityList;
    }

    /**
     *
     * @param entityClass
     * @param entityList
     * @return
     * @throws FactoryException
     */
    public EntityLoader createEntityLoader(Class entityClass,
            EntityList entityList) throws FactoryException{

        if(entityClass == null)
            throw new FactoryException("Falha ao criar EntityLoader."+
                    "\nvisualEntity não pode ser nulo.");

        if(entityList == null)
            throw new FactoryException("Falha ao criar EntityLoader."+
                    "\nentityList não pode ser nulo.");

        DefaultEntityLoader gl = new DefaultEntityLoader(entityClass);

        gl.setEntityList(entityList);

        return gl;

    }

    public SelectionLoader createSelectionLoader(Queryable queryable,
            EntitySelection entitySelection) throws FactoryException{

        if(queryable == null)
            throw new FactoryException("Falha ao criar SelectionLoader."+
                    "\nqueryable não pode ser nulo.");

        if(entitySelection == null)
            throw new FactoryException("Falha ao criar SelectionLoader."+
                    "\nentitySelection não pode ser nulo.");

        DefaultSelectionLoader dsl = new DefaultSelectionLoader();

        dsl.setQueryable(queryable);
        dsl.setEntitySelection(entitySelection);

        return dsl;
    }

    /**
     *
     * @param entityClass
     * @param entityList
     * @return
     * @throws FactoryException
     */
    public EntityLoader createSimpleEntityLoader(Class entityClass,
            EntityList entityList) throws FactoryException{

        if(entityClass == null)
            throw new FactoryException("Falha ao criar SimpleEntityLoader."+
                    "\nentityClass não pode ser nulo.");

        if(entityList == null)
            throw new FactoryException("Falha ao criar SimpleEntityLoader."+
                    "\nentityList não pode ser nulo.");

        SimpleEntityLoader el = new SimpleEntityLoader();

        el.setEntityClass(entityClass);
        el.setEntityList(entityList);

        return el;
    }

    /**
     *
     * @param entityClass
     * @return
     * @throws FactoryException
     */
    public Query createBaliQuery(Class entityClass) throws FactoryException{

        if(entityClass == null)
            throw new FactoryException("Falha ao criar BaliQuery."+
                    "\nentityClass não pode ser nulo.");

        return new DefaultBaliQuery(entityClass);
    }

    /**
     *
     * @param fieldName
     * @return
     * @throws FactoryException
     */
    public Criterion createBaliCriterion(String fieldName)
            throws FactoryException{

        if(fieldName == null)
            throw new FactoryException("Falha ao criar BaliCriterion."+
                    "\nfieldName não pode ser nulo.");
        
        return new DefaultBaliCriterion(fieldName);
    }

    /**
     *
     * @param entity
     * @param owner
     * @return
     * @throws FactoryException
     */
    public LineSelectionEvent createLineSelectedEvent(
            Serializable entity, Basic owner) throws FactoryException{

        if(entity == null)
            throw new FactoryException("Falha ao criar LineActivatedEvent."+
                    "\nentity não pode ser nulo.");

        if(owner == null)
            throw new FactoryException("Falha ao criar LineActivatedEvent."+
                    "\nsource não pode ser nulo.");

        DefaultLineSelectedEvent evt = new DefaultLineSelectedEvent(
                Event.LINE_SELECTED_EVENT, owner, entity);

        return evt;
    }

    public LineSelectionEvent createLastLineSelectedEvent(
            Serializable entity, Basic owner) throws FactoryException{

        if(entity == null)
            throw new FactoryException("Falha ao criar LineActivatedEvent."+
                    "\nentity não pode ser nulo.");

        if(owner == null)
            throw new FactoryException("Falha ao criar LineActivatedEvent."+
                    "\nsource não pode ser nulo.");

        DefaultLineSelectedEvent evt = new DefaultLineSelectedEvent(
                Event.LAST_LINE_SELECTED_EVENT, owner, entity);

        return evt;
    }

    /**
     *
     * @param entityList
     * @param initialIndex
     * @param finalIndex
     * @return
     * @throws FactoryException
     */
    public ListChangedEvent createListChangedEvent(EntityList entityList,
            int initialIndex, int finalIndex) throws FactoryException{

        if(entityList == null)
            throw new FactoryException("Falha ao criar ListChangedEvent."+
                    "\nentityList não pode ser nulo.");

        DefaultListChangedEvent evt = new DefaultListChangedEvent(
                entityList, entityList, initialIndex, finalIndex);

        return evt;
    }

    /**
     *
     * @param transaction
     * @param oldMode
     * @param mode
     * @return
     * @throws FactoryException
     */
    public TransactionModeChangedEvent createTransactionModeChangedEvent(
            Transaction transaction, int oldMode, int mode)
            throws FactoryException {

        if(transaction == null)
            throw new FactoryException(
                    "Falha ao criar TransactionModeChangedEvent."+
                    "\ntransaction não pode ser nulo.");

        DefaultTrasactionModeChangedEvent evt = new
                DefaultTrasactionModeChangedEvent(
                    transaction, transaction, oldMode, mode);

        return evt;
    }

    /**
     * 
     * @param va
     * @return
     * @throws FactoryException
     */
    public VisualActionEvent createVisualActionEvent(Basic owner, 
            VisualAction va) throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar VisualActionEvent."+
                    "\nOwner não pode ser nulo.");

        if(va == null)
            throw new FactoryException(
                    "Falha ao criar VisualActionEvent."+
                    "\nVisualAction não pode ser nulo.");

        return new DefaultVisualActionEvent(owner, va);
    }

    /**
     *
     * @param t
     * @return
     * @throws FactoryException
     */
    public BeforeValidateEvent createBeforeValidateEvent(Transaction t)
            throws FactoryException{

        if(t == null)
            throw new FactoryException(
                    "Falha ao criar BeforeValidateEvent.\n"+
                    "Transaction não pode ser nulo.");

        BeforeValidateEvent evt = new DefaultBeforeValidateEvent(t);

        return evt;
    }

    /**
     *
     * @param t
     * @return
     * @throws FactoryException
     */
    public BeforeSaveEvent createBeforeSaveEvent(Transaction t)
            throws FactoryException{

        if(t == null)
            throw new FactoryException(
                    "Falha ao criar BeforeSaveEvent.\n"+
                    "Transaction não pode ser nulo.");

        BeforeSaveEvent evt = new DefaultBeforeSaveEvent(t);

        return evt;
    }

    /**
     *
     * @param t
     * @return
     * @throws FactoryException
     */
    public AfterSaveEvent createAfterSaveEvent(Transaction t)
            throws FactoryException{

        if(t == null)
            throw new FactoryException(
                    "Falha ao criar AfterSaveEvent.\n"+
                    "Transaction não pode ser nulo.");

        AfterSaveEvent evt = new DefaultAfterSaveEvent(t);

        return evt;
    }

    /**
     * 
     * @param t
     * @return
     * @throws FactoryException
     */
    public BeforeDeleteEvent createBeforeDeleteEvent(Transaction t)
            throws FactoryException{

        if(t == null)
            throw new FactoryException(
                    "Falha ao criar BeforeDeleteEvent.\n"+
                    "Transaction não pode ser nulo.");

        BeforeDeleteEvent evt = new DefaultBeforeDeleteEvent(t);

        return evt;
    }

    /**
     * 
     * @param t
     * @return
     * @throws FactoryException
     */
    public AfterDeleteEvent createAfterDeleteEvent(Transaction t)
            throws FactoryException{

        if(t == null)
            throw new FactoryException(
                    "Falha ao criar AfterDeleteEvent.\n"+
                    "Transaction não pode ser nulo.");

        AfterDeleteEvent evt = new DefaultAfterDeleteEvent(t);

        return evt;
    }

    /**
     * 
     * @param va
     * @param ea
     * @return
     * @throws FactoryException
     */
    public BeforeConfirmEvent createBeforeConfirmEvent(VisualAction va,
            EntityActions ea)
            throws FactoryException{

        if(va == null)
            throw new FactoryException(
                    "Falha ao criar BeforeConfirmEvent.\n"+
                    "VisualAction não pode ser nulo.");

        if(ea == null)
            throw new FactoryException(
                    "Falha ao criar BeforeConfirmEvent.\n"+
                    "EntityActions não pode ser nulo.");

        BeforeConfirmEvent evt = new DefaultBeforeConfirmEvent(va, ea);

        return evt;
    }

    /**
     * 
     * @param va
     * @param ea
     * @return
     * @throws FactoryException
     */
    public BeforeCancelEvent createBeforeCancelEvent(VisualAction va,
            EntityActions ea)
            throws FactoryException{

        if(va == null)
            throw new FactoryException(
                    "Falha ao criar BeforeCancelEvent.\n"+
                    "VisualAction não pode ser nulo.");

        if(ea == null)
            throw new FactoryException(
                    "Falha ao criar BeforeCancelEvent.\n"+
                    "EntityActions não pode ser nulo.");

        BeforeCancelEvent evt = new DefaultBeforeCancelEvent(va, ea);

        return evt;
    }

    /**
     *
     * @param ep
     * @return
     * @throws FactoryException
     */
    public AfterBindEvent createAfterBindEvent(EntityPanel ep)
            throws FactoryException{

        if(ep == null)
            throw new FactoryException(
                    "Falha ao criar AfterBindEvent.\n"+
                    "EntityPanel não pode ser nulo.");

        AfterBindEvent evt = new DefaultAfterBindEvent(ep);

        return evt;
    }

    /**
     * 
     * @param ep
     * @return
     * @throws FactoryException
     */
    public AfterUnbindEvent createAfterUnbindEvent(EntityPanel ep)
            throws FactoryException{

        if(ep == null)
            throw new FactoryException(
                    "Falha ao criar AfterUnbindEvent.\n"+
                    "EntityPanel não pode ser nulo.");

        AfterUnbindEvent evt = new DefaultAfterUnbindEvent(ep);

        return evt;
    }

    /**
     * 
     * @param el
     * @return
     * @throws FactoryException
     */
    public BeforeLoadEvent createBeforeLoadEvent(EntityLoader el, Query query)
            throws FactoryException{

        if(el == null)
            throw new FactoryException(
                    "Falha ao criar BeforeLoadEvent.\n"+
                    "EntityLoader não pode ser nulo.");

        if(query == null)
            throw new FactoryException(
                    "Falha ao criar BeforeLoadEvent.\n"+
                    "Query não pode ser nulo.");

        BeforeLoadEvent evt = new DefaultBeforeLoadEvent(el,query);

        return evt;
    }

    /**
     * 
     * @param el
     * @return
     * @throws FactoryException
     */
    public BeforeIterateEvent createBeforeIterateEvent(EntityLoader el)
            throws FactoryException{

        if(el == null)
            throw new FactoryException(
                    "Falha ao criar BeforeIterateEvent.\n"+
                    "EntityLoader não pode ser nulo.");

        BeforeIterateEvent evt = new DefaultBeforeIterateEvent(el);

        return evt;
    }

    /**
     * 
     * @param el
     * @return
     * @throws FactoryException
     */
    public AfterLoadEvent createAfterLoadEvent(EntityLoader el, 
            List<Serializable> entities) throws FactoryException{

        if(el == null)
            throw new FactoryException(
                    "Falha ao criar AfterLoadEvent.\n"+
                    "EntityLoader não pode ser nulo.");

        AfterLoadEvent evt = new DefaultAfterLoadEvent(el,entities);

        return evt;
    }

    /**
     * 
     * @param owner
     * @param gc
     * @return
     * @throws FactoryException
     */
    public ColumnSelectedEvent createColumnSelectedEvent(Basic owner,
            GridColumn oldGC, GridColumn gc) throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar ColumnSelectedEvent.\n"+
                    "Owner não pode ser nulo.");

        if(gc == null)
            throw new FactoryException(
                    "Falha ao criar ColumnSelectedEvent.\n"+
                    "GridColumn não pode ser nulo.");

        ColumnSelectedEvent evt = 
                new DefaultColumnSelectedEvent(owner, oldGC, gc);

        return evt;
    }

    /**
     * 
     * @param owner
     * @param gc
     * @return
     * @throws FactoryException
     */
    public LastColumnSelectedEvent createLastColumnSelectedEvent(Basic owner,
            GridColumn oldGC, GridColumn gc) throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar LastColumnSelectedEvent.\n"+
                    "Owner não pode ser nulo.");

        if(gc == null)
            throw new FactoryException(
                    "Falha ao criar LastColumnSelectedEvent.\n"+
                    "GridColumn não pode ser nulo.");

        LastColumnSelectedEvent evt =
                new DefaultLastColumnSelectedEvent(owner, oldGC, gc);

        return evt;
    }

    /**
     * 
     * @param owner
     * @param gc
     * @return
     * @throws FactoryException
     */
    public LastEditableColumnSelectedEvent
            createLastEditableColumnSelectedEvent(Basic owner,
            GridColumn oldGC, GridColumn gc) throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar LastEditableColumnSelectedEvent.\n"+
                    "Owner não pode ser nulo.");

        if(gc == null)
            throw new FactoryException(
                    "Falha ao criar LastEditableColumnSelectedEvent.\n"+
                    "GridColumn não pode ser nulo.");

        LastEditableColumnSelectedEvent evt =
                new DefaultLastEditableColumnSelectedEvent(owner, oldGC, gc);

        return evt;
    }

    /**
     * 
     * @param owner
     * @param fl
     * @return
     * @throws FactoryException
     */
    public FieldActionEvent createFieldActionEvent(FieldAction owner,
            FieldLayout fl) throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar FieldActionEvent.\n"+
                    "FieldAction não pode ser nulo.");

        if(fl == null)
            throw new FactoryException(
                    "Falha ao criar FieldActionEvent.\n"+
                    "GriFieldLayoutdColumn não pode ser nulo.");

        FieldActionEvent evt = new DefaultFieldActionEvent(owner, fl);

        return evt;
    }

    /**
     * 
     * @param owner
     * @param sr
     * @return
     * @throws FactoryException
     */
    public SyncEvent createSyncEvent(Basic owner, SyncResult sr)
            throws FactoryException{

        if(owner == null)
            throw new FactoryException(
                    "Falha ao criar SyncEvent.\n"+
                    "Owner não pode ser nulo.");

        if(sr == null)
            throw new FactoryException(
                    "Falha ao criar SyncEvent.\n"+
                    "SyncResult não pode ser nulo.");

        SyncEvent evt = new DefaultSyncEvent(owner, sr);

        return evt;
    }

    /**
     * 
     * @param fi
     * @return
     * @throws FactoryException
     */
    public GotFocusEvent createGotFocusEvent(FieldInput fi)
            throws FactoryException{

        if(fi == null)
            throw new FactoryException(
                    "Falha ao criar GotFocusEvent.\n"+
                    "FieldInput não pode ser nulo.");

        GotFocusEvent evt = new DefaultGotFocusEvent(fi);

        return evt;
    }

    /**
     * 
     * @param fi
     * @return
     * @throws FactoryException
     */
    public LostFocusEvent createLostFocusEvent(FieldInput fi)
            throws FactoryException{

        if(fi == null)
            throw new FactoryException(
                    "Falha ao criar LostFocusEvent.\n"+
                    "FieldInput não pode ser nulo.");

        LostFocusEvent evt = new DefaultLostFocusEvent(fi);

        return evt;
    }

    /**
     * 
     * @param fi
     * @return
     * @throws FactoryException
     */
    public FocusTraversalEvent createFocusTraversalEvent(FieldInput fi)
            throws FactoryException{

        if(fi == null)
            throw new FactoryException(
                    "Falha ao criar FocusTraversalEvent.\n"+
                    "FieldInput não pode ser nulo.");

        FocusTraversalEvent evt = new DefaultFocusTraversalEvent(fi);

        return evt;
    }

    /**
     * 
     * @param entityClass
     * @param c
     * @return
     * @throws FactoryException
     */
    public GridColumn[] createGridColumn(Class entityClass,
            ColumnUtilities c) throws FactoryException{

        if(entityClass == null)
            throw new FactoryException("Falha ao criar BaliGridColumn."+
                    "\nentityClass não pode ser nulo.");

        if(c == null)
            throw new FactoryException("Falha ao criar BaliGridColumn."+
                    "\nColumnUtilities não pode ser nulo.");

        try{
            switch(c.type){
                case ColumnUtilities.DEFINITION:

                    DefaultBaliColumn col = new DefaultBaliColumn();

                    col.setColumnClass(c.field.getType());
                    col.setColumnEditable(c.editable);
                    col.setColumnName(c.name);
                    col.setWidth(c.width);
                    col.setGetMethod(c.getGetMethod(entityClass));
                    col.setSetMethod(c.getSetMethos(entityClass));
                    
                    return new GridColumn[]{col};

                case ColumnUtilities.EMBEDDED:

                    // cria array das colunas
                    GridColumn[] cols = new GridColumn[c.count()];
                    int ic = 0;

                    // para cada coluna emcapsulada
                    for (ColumnUtilities colEmb : c.embedded) {
                        // cria colunas
                        GridColumn[] colsEmb =
                                createGridColumn(c.field.getType(),colEmb);
                        // para cada coluna criada
                        for (GridColumn bgc : colsEmb) {
                            // adiciona chamada para a coluna
                            bgc.addCalls(c.getGetMethod(entityClass));
                            // adiciona ao array das colunas
                            cols[ic] = bgc;
                            ic++;
                        }
                    }
                    return cols;
                case ColumnUtilities.SELECTION:
                    throw new FactoryException(
                            "Falha ao criar BaliGridColumn.\n" +
                            "SELECTION não implementado ainda!");
            }
        }catch(Exception ex){
            throw new FactoryException("Falha ao criar BaliGridColumn.\n" +
                    ex.getMessage(),ex.getCause());
        }
        
        return null;
    }


    /**
     *
     * @param map
     * @param method
     * @param withParm
     * @return
     * @throws FactoryException
     */
    public org.balisunrise.common.Action createAction(Object map, Method method,
            boolean withParm) throws FactoryException{

        if(map == null)
            throw new FactoryException("Falha ao criar BaliEvent."+
                    "\nmap não pode ser nulo.");

        if(method == null)
            throw new FactoryException("Falha ao criar BaliEvent."+
                    "\nmethod não pode ser nulo.");

        // Ação
        DirectAction da = new DirectAction(map,method,withParm);
        return da;
    }

    /**
     * Cria uma nova instância de uma VisualEntity pelo tipo.
     * É utilizado o VisualEntityManager do Application para criação.
     *
     * @param type tipo da VisualEntity a ser criado.
     * @param entityClass classe da entidade.
     * @param alias alias para criação da VisualEntity.
     * @return uma nova instância de VisualEntity do tipo recebido por parâmetro.
     * @throws BuilderException Retornado pelos Builders.
     */
    public VisualEntity createVisualEntity(VisualEntityType type,
            Class entityClass, String alias) throws BuilderException {

        if (type == null)
            throw new BuilderException("type is null");

        VisualEntityManager vem = Application.get().getVisualEntityManager();

        switch (type) {
            case TRANSACTION:
                return vem.getTransactionBuilder().create(entityClass, alias);
            case SEARCH:
                return vem.getSearchBuilder().create(entityClass, alias);
            case EDITOR:
                return vem.getEditorBuilder().create(entityClass, alias);
            case TRANSACTION_ACTIONS:
                return vem.getTransactionActionBuilder().create(entityClass, alias);
            case ENTITY_FILTER:
                return vem.getEntityFilterBuilder().create(entityClass, alias);
            case ENTITY_GRID:
                return vem.getEntityGridBuilder().create(entityClass, alias);
            case ENTITY_ACTIONS:
                return vem.getEntityActionBuilder().create(entityClass, alias);
            case MESSENGER:
                return vem.getMessengerBuilder().create(entityClass, alias);
            case ENTITY_PANEL:
                return vem.getEntityPanelBuilder().create(entityClass, alias);
            case ENTITY_SELECTION:
                return vem.getEntitySelectionBuilder().create(entityClass, alias);
            case GRID:
                return vem.getBaliGridBuilder().create(entityClass, alias);
            default:
                throw new AssertionError();
        }
    }
}
